#!/usr/bin/python
# -*- coding: utf-8 -*-
# Description: 文件辅助方法

import os
import struct
import shutil
from seblog.common_logic.utility.const import FileType
from seblog.common_logic.utility import peutil
from seblog.common_logic.utility import file_utility
from seblog.common_logic.utility.md5mgr import mkmd5fromfile

# Archive
ZIP_HEADER_SIGNATURE = ((0x04034b50,),) #zip
RAR_HEADER_SIGNATURE = ((0x21726152,),) #rar
CAB_HEADER_SIGNATURE = ((0x4643534D,),) #cab
_7Z_HEADER_SIGNATURE = ((0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C),) # 7z '7', 'z', 0xBC, 0xAF, 0x27, 0x1C
TAR_HEADER_SIGNATURE = ((0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00,), 
                        (0x47, 0x4E, 0x55, 0x74, 0x61, 0x72, 0x20, 0x00,)) #tar
#/* The magic field is filled with this if uname and gname are valid. */
#define    TMAGIC    "ustar  "        /* 7 chars and a null */
#/* The magic field is filled with this if this is a GNU format dump entry */
#define    GNUMAGIC  "GNUtar "        /* 7 chars and a null */

# Image
JPG_HEADER_SIGNATURE = ((0xD8FF,),)     #jpg
BMP_HEADER_SIGNATURE = ((0x4D42,),) #bmp
GIF8_HEADER_SIGNATURE = ((0x38464947,),) #gif8x
PNG_HEADER_SIGNATURE = ((0x0A1A0A0D474E5089,),) #png
# file
PDF_HEADER_SIGNATURE = ((0x46445025,),) #PDF

know_types = set((FileType.DLL,
                 FileType.EXE,
                 FileType.ZIP,
                 FileType.RAR,
                 FileType.CAB,
                 FileType._7Z,
                 FileType.TAR,
                 FileType.JPG,
                 FileType.BMP,
                 FileType.GIF,
                 FileType.PNG,
                 FileType.PDF))

header_signature_format = (
       (FileType.ZIP, ZIP_HEADER_SIGNATURE, 'L', 4, 0),
       (FileType.RAR, RAR_HEADER_SIGNATURE, 'L', 4, 0),
       (FileType.CAB, CAB_HEADER_SIGNATURE, 'L', 4, 0),
       (FileType._7Z, _7Z_HEADER_SIGNATURE, 'BBBBBB', 6, 0),
       (FileType.TAR, TAR_HEADER_SIGNATURE, 'BBBBBBBB', 8, 0x101),
       (FileType.JPG, JPG_HEADER_SIGNATURE, 'H', 2, 0),
       (FileType.BMP, BMP_HEADER_SIGNATURE, 'H', 2, 0),
       (FileType.GIF, GIF8_HEADER_SIGNATURE, 'L', 4, 0),
       (FileType.PNG, PNG_HEADER_SIGNATURE, 'Q', 8, 0),
       (FileType.PDF, PDF_HEADER_SIGNATURE, 'L', 4, 0),
        )

def get_filetype(filepath):
    """根据各类型文件格式获取文件类型, PS:只是估计，无法100%确定，但能100%否定。
    return filetype
    
    >>> path = 'D:/testsamples'
    >>> FileType.Nofile == get_filetype(path + '/empty/awfafafwafawfw.dll')
    True
    >>> FileType.Empty == get_filetype(path + '/empty/ee017db73ed184cc0aabd2dad32be81e.dll')
    True
    >>> FileType.EXE == get_filetype(path + '/exe/0c045bbdd7b74f4d4d50197a0bba2178.exe')
    True
    >>> FileType.DLL == get_filetype(path + '/dll/0a5351ea20a39e628c7c0118a82dccd4.dll')
    True
    >>> FileType.RAR == get_filetype(path + '/rar/zeng7.rar')
    True
    >>> FileType.ZIP == get_filetype(path + '/zip/curl-7.18.2-win32-ssl-sspi.zip')
    True
    >>> FileType.CAB == get_filetype(path + '/cab/engine32.cab')
    True
    >>> FileType._7Z == get_filetype(path + '/7z/html_div.7z')
    True
    >>> FileType.TAR == get_filetype(path + '/tar/pefile-1.2.9.1.tar')
    True
    >>> FileType.JPG == get_filetype(path + '/jpg/3c9f8d47a25240252cdfc11192b2da5c.jpg')
    True
    >>> FileType.BMP == get_filetype(path + '/bmp/test.bmp')
    True
    >>> FileType.GIF == get_filetype(path + '/gif/sdfsfd.gif')
    True
    >>> FileType.PNG == get_filetype(path + '/png/a2_005.png')
    True
    >>> FileType.Empty == get_filetype(path + '/empty/blank.txt')
    True
    >>> FileType.PDF == get_filetype(path + '/pdf/asn1berder.pdf')
    True
    """
    if not os.path.exists(filepath) or not os.path.isfile(filepath):
        return FileType.Nofile
    if os.path.getsize(filepath) == 0:
        return FileType.Empty
    ispe, filetype = peutil.is_pe(filepath)
    if ispe:
        return filetype
    try:
        f = open(filepath, 'rb')
        try:
            for filetype, signature, format, size, offset in header_signature_format:
                f.seek(offset)
                try:
                    header_signature = struct.unpack(format, f.read(size))
                    if header_signature in signature:
                        return filetype
                except struct.error:
                    pass
            ext = os.path.splitext(filepath)[1]
            if ext:
                ext = ext[1:].upper()
                if ext in FileType.__dict__:
                    filetype = FileType.__dict__[ext]
                    if filetype not in know_types: # 若非已知文件，则根据后缀名来判断
                        return filetype
            return FileType.Unknow
        finally:
            f.close()
    except IOError, e:
        if e.errno == 13: #Permission denied
            return FileType.Permdenied
        raise

def is_archive(filepath):
    """判断知道文件是否压缩包"""
    filetype = get_filetype(filepath)
    return 100 < filetype < 200 and filetype != FileType.MSP

def is_image(filepath):
    """判断指定路径的文件是否图片"""
    filetype = get_filetype(filepath)
    return 200 < filetype < 300

def digital_sign_verify(filepath):
    """验证文件的数字签名是否正确
    
    基于'FileDigitalSignVerify.EXE'命令行工具: http://www.kztechs.com/filedsv/
    
    return True/False, result_code, name of signer
    """
    cmd = '%s %s' % (os.path.join(os.path.dirname(__file__), 'FileDigitalSignVerify.EXE'),
                     filepath)
    ps = os.popen(cmd)
    ps.readline()
    ps.readline()
    ps.readline()
    ps.readline()
    ps.readline()
    ps.readline()
    ps.readline()
    ps.readline()
    ps.readline()
    result = ps.readline().strip()
    if result:
        if '\t' not in result:
            rs = [r.strip() for r in result.split('-')]
            code, signer, filepath = rs[0], None, rs[1]
        else:
            code, signer, filepath = tuple(result.split('\t'))
            signer = signer.strip()
        ps.close()
        return '0x00000000' == code, code, signer
    else:
        return False, None, None #文件不存在

def walkfiles(rootpaths, exclude_paths=()):
    """使用生成器遍历指定文件夹"""
    if isinstance(rootpaths, basestring):
        rootpaths = [rootpaths]
    for rootpath in rootpaths:
        if not os.path.isdir(rootpath) or rootpath.find('System Volume Information') > 0:
            return
        exclude = False
        for exclude_path in exclude_paths:
            if rootpath.startswith(exclude_path):
                exclude = True
                break
        if not exclude:
            for path in os.listdir(rootpath):
                fullpath = os.path.normpath(os.path.join(rootpath, path))
                if os.path.isdir(fullpath):
                    for x in walkfiles([fullpath], exclude_paths):
                        yield x
                else:
                    yield fullpath

def copy(src, dst, override=False, ismove=False):
    """复制src到dst，可指定是否覆盖相同名称的文件
    dst: \或/结束，则认为dst是目录，否则为文件
    
    return dst file full path
    """
    if dst.endswith(u'/') or dst.endswith(u'\\') or os.path.isdir(dst):
        filename = os.path.basename(src)
        dst_filename = os.path.join(dst, filename)
    else:
        dst_filename = dst
        dst = os.path.dirname(dst)
    file_utility.ensure_dir_exists(dst)
    name_ext = os.path.splitext(dst_filename)
    samecount = 0
    while True:
        if samecount > 0:
            basename = u'%s(%d)%s' % (name_ext[0], samecount, name_ext[1])
            dst_filename = os.path.join(dst, basename)
        if not override and os.path.exists(dst_filename):
            #判断已存在文件是否与src的相同
            if mkmd5fromfile(dst_filename) == mkmd5fromfile(src):
                break
            samecount += 1
            continue
        if ismove:
            shutil.move(src, dst_filename)
        else:
            shutil.copy2(src, dst_filename)
        break
    return dst_filename.replace(u'\\', u'/')

def getsize(filepath):
    size = os.path.getsize(filepath)
    unit = u'B'
    if size > 1024:
        unit = u'KB'
        size /= 1024.0
        if size > 1024:
            unit = u'MB'
            size /= 1024.0
    return u'%.2f%s' % (size, unit)
    
def __doc_test():
    import doctest
    doctest.testmod()
    
if __name__ == '__main__':
    print get_filetype(ur'D:\mercury\sepython86\djangoproject\seblog_project\seblog\web\frontapp\media\images\icons.gif')
#    __doc_test()
#    print get_filetype('c:/DetectFileType.rar')
#    print digital_sign_verify('e:/softs/VISTA_SP1_OEM_CHS_UM_28in1.ISO')
#    print digital_sign_verify('c:/aduserinfo.exe2')
#    print digital_sign_verify('c:/aduserinfo.exe')
#    print digital_sign_verify('c:/filedsv.zip')
#    print digital_sign_verify('c:/AcroRd32.exe')
#    print digital_sign_verify('c:/VS80sp1-KB926601-X86-ENU.exe')
#    for filepath in [os.path.join('D:/testsamples/exe', filename) for filename in os.listdir('D:/testsamples/exe')]:
#        print digital_sign_verify(filepath)
#    copy(__file__, u'c:/testdddddddd/testcopy.txt')
    